/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.stat.descriptive;

import genomemap.data.Clone;
import genomemap.data.PhysicalMapService;
import genomemap.tagging.LinkingCloneStrategy;

import javautil.collections.ArrayUtil;
import javautil.io.IOUtil;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.math3.exception.DimensionMismatchException;
import org.apache.commons.math3.stat.descriptive.MultivariateSummaryStatistics;

import java.text.DecimalFormat;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author stewari1
 */
public class DNARepeatsFreqDist {

    /** Field description */
    private static final int DIST_TYPE_SRA_ONLY = 0;

    /** Field description */
    public static final int DIST_TYPE_SRA_TRANSITION = 1;

    /** Field description */
    private int distType = 0;

    /** Field description */
    private int[][] freqTable = null;

    /** Field description */
    private String[] repeatCategories;

    /** Field description */
    private String tableTitle;

    /** Field description */
    private String[] categories;

    /** Field description */
    private Clone[][] clones;

    /**
     * Constructs ...
     *
     * @param ch_ids description
     * @param probeOrders description
     */
    public DNARepeatsFreqDist(int[] ch_ids, int[][] probeOrders) {

        clones        = new Clone[ch_ids.length][];
        categories    = new String[ch_ids.length + 1];
        categories[0] = "Chromosome";

        for (int i = 0; i < ch_ids.length; i++) {

            int ch_id = ch_ids[i];

            clones[i] = PhysicalMapService.getMinimalPathClones(ch_id, probeOrders[i],
                    new LinkingCloneStrategy());
            categories[i + 1] = "" + ch_id;
        }
    }

    /**
     * Constructs ...
     *
     * @param clones description
     * @param categories description
     * @param categoryHeader description
     */
    public DNARepeatsFreqDist(Clone[][] clones, String[] categories, String categoryHeader) {

        if (clones.length != categories.length) {

            throw new Error(
                "categories length is not same as the first dimension of clones 2dim array");
        }

        this.clones        = clones;
        this.categories    = new String[categories.length + 1];
        this.categories[0] = (categoryHeader == null) ? "Categories" : categoryHeader;

        for (int i = 0; i < categories.length; i++) {

            this.categories[i + 1] = categories[i];
        }
    }

    /**
     * Constructs ...
     *
     * @param cloneArray description
     * @param category description
     */
    public DNARepeatsFreqDist(Clone[] cloneArray, String category) {

        this.clones    = new Clone[1][];
        categories     = new String[1];
        this.clones[0] = cloneArray;
        categories[0]  = (category == null) ? "" : category;
    }

    public String getFreqTableString() {

        freqTable = getFreqTable();    // table is computed if it has not been yet

        StringBuilder strBuilder = new StringBuilder();

        strBuilder.append(tableTitle + " : " + "Frequencey Distribution:  \n");
        strBuilder.append(getDistTableString(ArrayUtil.getDoubleArray(freqTable)));

        return strBuilder.toString();
    }

    public String getFreqPercentTableString() {

        freqTable = getFreqTable();    // table is computed if it has not been yet

        StringBuilder strBuilder = new StringBuilder();

        strBuilder.append(tableTitle + " : " + "Percent Frequencey Distribution:  \n");


        // Note: freqTable can`t be ragged. so we can safely use freqTable[0].length in the following
        double[][] freqPercentTable = new double[freqTable.length][freqTable[0].length];

        for (int i = 0; i < freqTable.length; i++) {

            for (int j = 0; j < freqTable[0].length; j++) {

                freqPercentTable[i][j] = freqTable[i][j]
                                         / (double) freqTable[i][freqTable[0].length - 1] * 100;
            }
        }

        strBuilder.append(getDistTableString(freqPercentTable));

        return strBuilder.toString();
    }

    /**
     * Method description
     *
     * @param table description
     *
     * @return description
     */
    private String getDistTableString(double[][] table) {

        DecimalFormat decimalFormat = new DecimalFormat("0.##");
        String[][] result           = new String[clones.length + 2][repeatCategories.length + 2];

        for (int i = 0; i < result.length; i++) {

            if (i == 0) {

                result[0][0] = categories[0];

                for (int j = 0; j < repeatCategories.length; j++) {

                    result[0][j + 1] = repeatCategories[j];
                }

                result[0][repeatCategories.length + 1] = "Total";

            } else {

                if (i < result.length - 1) result[i][0] = categories[i];
                if (i == result.length - 1) result[i][0] = "Total";

                for (int j = 1; j < result[i].length; j++) {


                    // i -1 and j - 1 for accounting header and the first variable col
                    result[i][j] = decimalFormat.format(table[i - 1][j - 1]);
                }
            }
        }

        return IOUtil.toString(result, 2, 1, null);
    }

    int[][] getFreqTable() {

        if (freqTable == null) {
            computeFreqTable();
        }

        return freqTable;
    }

    /**
     * Method description
     */
    private void computeFreqTable() {

        switch (distType) {

            case DIST_TYPE_SRA_ONLY :
                compute_SRA_FreqTable();

                break;

            case DIST_TYPE_SRA_TRANSITION :
                compute_SRA_TRANsition_FreqTable();

                break;
        }
    }

    /**
     * Method description
     */
    private void compute_SRA_FreqTable() {


        // Note: +1 is for total column
        freqTable        = new int[clones.length + 1][3 + 1];
        repeatCategories = new String[] { "S", "R", "A" };
        tableTitle       = "DNA repeats in S, R and A clones";

        MultivariateSummaryStatistics masterSummary = new MultivariateSummaryStatistics(4, true);

        for (int i = 0; i < clones.length; i++) {

            try {

                MultivariateSummaryStatistics summaryStat = new MultivariateSummaryStatistics(3,
                                                                true);

                for (int cloneIndex = 0; cloneIndex < clones[i].length; cloneIndex++) {

                    Clone clone        = clones[i][cloneIndex];
                    double[] signature = { 0, 0, 0 };

                    switch (clone.getCloneType()) {

                        case Clone.S_CLONE :
                            signature[0] = 1;

                            break;

                        case Clone.R_CLONE :
                            signature[1] = 1;

                            break;

                        case Clone.A_CLONE :
                            signature[2] = 1;

                            break;
                    }

                    summaryStat.addValue(signature);
                }

                double[] total = summaryStat.getSum();

                for (int j = 0; j < 3; j++) {

                    freqTable[i][j] = (int) total[j];
                }

                freqTable[i][3] = (int) summaryStat.getN();

                masterSummary.addValue(ArrayUtil.getDoubleArray(freqTable[i]));

            } catch (DimensionMismatchException ex) {
                Logger.getLogger(DNARepeatsFreqDist.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        freqTable[clones.length] = ArrayUtil.getIntegerArray(ArrayUtils.add(masterSummary.getSum(),
                masterSummary.getN()));
    }

    /**
     * Method description
     */
    private void compute_SRA_TRANsition_FreqTable() {


        // Note: +1 is for total column
        freqTable        = new int[clones.length + 1][9 + 1];
        repeatCategories = new String[] {
            "SS", "SR", "SA", "RS", "RR", "RA", "AS", "AR", "AA"
        };
        tableTitle       =
            "DNA repeats in transition categories SS, SR, SA, RS, RR, RA, AS, AR, AA";

        MultivariateSummaryStatistics masterSummary = new MultivariateSummaryStatistics(10, true);

        for (int i = 0; i < clones.length; i++) {

            try {

                MultivariateSummaryStatistics summaryStat = new MultivariateSummaryStatistics(9,
                                                                true);

                for (int cloneIndex = 1; cloneIndex < clones[i].length; cloneIndex++) {

                    Clone clone1       = clones[i][cloneIndex - 1];
                    Clone clone2       = clones[i][cloneIndex];
                    double[] signature = {
                        0, 0, 0, 0, 0, 0, 0, 0, 0
                    };
                    int clone1Type     = clone1.getCloneType();
                    int clone2Type     = clone2.getCloneType();

                    if ((clone1Type == Clone.S_CLONE) && (clone2Type == Clone.S_CLONE)) {
                        signature[0] = 1;
                    } else if ((clone1Type == Clone.S_CLONE) && (clone2Type == Clone.R_CLONE)) {
                        signature[1] = 1;
                    } else if ((clone1Type == Clone.S_CLONE) && (clone2Type == Clone.A_CLONE)) {
                        signature[2] = 1;
                    } else if ((clone1Type == Clone.R_CLONE) && (clone2Type == Clone.S_CLONE)) {
                        signature[3] = 1;
                    } else if ((clone1Type == Clone.R_CLONE) && (clone2Type == Clone.R_CLONE)) {
                        signature[4] = 1;
                    } else if ((clone1Type == Clone.R_CLONE) && (clone2Type == Clone.A_CLONE)) {
                        signature[5] = 1;
                    } else if ((clone1Type == Clone.A_CLONE) && (clone2Type == Clone.S_CLONE)) {
                        signature[6] = 1;
                    } else if ((clone1Type == Clone.A_CLONE) && (clone2Type == Clone.R_CLONE)) {
                        signature[7] = 1;
                    } else if ((clone1Type == Clone.A_CLONE) && (clone2Type == Clone.A_CLONE)) {
                        signature[8] = 1;
                    }

                    summaryStat.addValue(signature);
                }

                double[] total = summaryStat.getSum();

                for (int j = 0; j < 9; j++) {

                    freqTable[i][j] = (int) total[j];
                }

                freqTable[i][9] = (int) summaryStat.getN();

                masterSummary.addValue(ArrayUtil.getDoubleArray(freqTable[i]));

            } catch (DimensionMismatchException ex) {
                Logger.getLogger(DNARepeatsFreqDist.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        freqTable[clones.length] = ArrayUtil.getIntegerArray(ArrayUtils.add(masterSummary.getSum(),
                masterSummary.getN()));
    }

    public void setDistType(int distType) {
        this.distType = distType;
    }
}
